using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
using System.Query;

namespace PowerStructure
{
    public interface IValueMemberInfo
    {
        object GetValue(object obj);
        void SetValue(object obj, object value);
    }
    public class PropertyInfo2 : IValueMemberInfo
    {
        PropertyInfo _pi;

        public PropertyInfo2 (PropertyInfo pi)
	    {
            _pi = pi;
	    }
        #region IValueMemberInfo Members

        public object GetValue(object obj)
        {
            return _pi.GetValue(obj, null);
        }

        public void SetValue(object obj, object value)
        {
            _pi.SetValue(obj, value, null);
        }

        #endregion
    }
    public class FieldInfo2 : IValueMemberInfo
    {
        FieldInfo _fi;

        public FieldInfo2 (FieldInfo fi)
	    {
            _fi = fi;
	    }
        #region IValueMemberInfo Members

        public object GetValue(object obj)
        {
            return _fi.GetValue(obj);
        }

        public void SetValue(object obj, object value)
        {
            _fi.SetValue(obj, value);
        }

        #endregion
    }
    
        
    
    public static class ReflectionHelper
    {
        public static IEnumerable<IValueMemberInfo> GetValueMembers(Type type)
        {
            foreach (PropertyInfo pi in type.GetProperties())
                yield return new PropertyInfo2(pi);

            foreach (FieldInfo fi in type.GetFields())
                yield return new FieldInfo2(fi);
        }
        
        public static IValueMemberInfo GetValueMember(this Type type, string name)
        {
            if(type.GetProperty(name) != null )
                return new PropertyInfo2(type.GetProperty(name));
            if(type.GetField(name) != null )
                return new FieldInfo2(type.GetField(name));
            return null;
        }
        
        public static Lookup<FieldInfo, T> FieldsWithAttribute<T>(this Type data)
        {
            List<KeyValuePair<FieldInfo, T>> result = new List<KeyValuePair<FieldInfo, T>>();
            
            foreach (FieldInfo fi in data.GetFields())
            {
                foreach (T t in fi.GetCustomAttributes(false).OfType<T>())
                {
                    result.Add(new KeyValuePair<FieldInfo, T>( fi, t));
                }
            }
            return result.ToLookup(keyvalue => keyvalue.Key,  keyvalue => keyvalue.Value);
        }

        
        public static Lookup<T, FieldInfo> FieldsWithAttribute2<T>(this Type data)
        {
            FieldInfo[] Elem1 = data.GetFields();
            IEnumerable<T> Elem2 = Elem1.SelectMany(fi => fi.GetCustomAttributes(false).OfType<T>()).Distinct();

            List<KeyValuePair<T, FieldInfo>> result = new List<KeyValuePair<T, FieldInfo>>();
            
            foreach (T t in Elem2)
            {
                foreach (FieldInfo fi in Elem1)
                {
                    if(fi.GetCustomAttributes(false).OfType<T>().Contains(t))
                        result.Add(new KeyValuePair<T, FieldInfo>(t, fi));
                }
            }
            return result.ToLookup(keyvalue => keyvalue.Key,  keyvalue => keyvalue.Value);
        }        

        public static Lookup<K, KeyValuePair<T, FieldInfo>> FieldsWithAttribute3<T, K>(this Type data, Func<T, K> Keymaker)
        {
            FieldInfo[] Elem1 = data.GetFields();
            IEnumerable<T> Elem2 = Elem1.SelectMany(fi => fi.GetCustomAttributes(false).OfType<T>());

            List<KeyValuePair<T, FieldInfo>> result = new List<KeyValuePair<T, FieldInfo>>();
            
            foreach (T t in Elem2)
            {
                foreach (FieldInfo fi in Elem1)
                {
                    if(fi.GetCustomAttributes(false).OfType<T>().Contains(t))
                        result.Add( new KeyValuePair<T, FieldInfo>(t, fi));
                }
            }
            return result.ToLookup(keyvalue => Keymaker.Invoke(keyvalue.Key),  keyvalue => keyvalue);
        }        
    }
}
